<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>OAuth 2.0 Resource Server Opaque Token :: Spring Security</title>
<link rel="canonical" href="../../../../servlet/oauth2/resource-server/opaque-token.html">
<link rel="prev" href="jwt.html">
<link rel="next" href="multitenancy.html">
<meta name="generator" content="Antora 3.0.0">
<link rel="stylesheet" href="../../../../_/css/site.css">
<link href="../../../../_/img/favicon.ico" rel='shortcut icon' type='image/vnd.microsoft.icon'>
<link rel="stylesheet" href="../../../../_/css/vendor/docsearch.min.css">

<script>var uiRootPath = '../../../../_'</script>
</head>
<body class="article">
<header class="header">
<nav class="navbar">
<div class="navbar-brand">
<a class="navbar-item" href="https://spring.io">
<img id="springlogo" class="block" src="../../../../_/img/spring-logo.svg" alt="Spring">
</a>
<button class="navbar-burger" data-target="topbar-nav">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div id="topbar-nav" class="navbar-menu">
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="opaque-token.html#">Why Spring</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="https://spring.io/why-spring">Overview</a>
<a class="navbar-item" href="https://spring.io/microservices">Microservices</a>
<a class="navbar-item" href="https://spring.io/reactive">Reactive</a>
<a class="navbar-item" href="https://spring.io/event-driven">Event Driven</a>
<a class="navbar-item" href="https://spring.io/cloud">Cloud</a>
<a class="navbar-item" href="https://spring.io/web-applications">Web Applications</a>
<a class="navbar-item" href="https://spring.io/serverless">Serverless</a>
<a class="navbar-item" href="https://spring.io/batch">Batch</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="opaque-token.html#">Learn</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="https://spring.io/learn">Overview</a>
<a class="navbar-item" href="https://spring.io/quickstart">Quickstart</a>
<a class="navbar-item" href="https://spring.io/guides">Guides</a>
<a class="navbar-item" href="https://spring.io/blog">Blog</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="opaque-token.html#">Projects</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="https://spring.io/projects">Overview</a>
<a class="navbar-item" href="https://spring.io/projects/spring-boot">Spring Boot</a>
<a class="navbar-item" href="https://spring.io/projects/spring-framework">Spring Framework</a>
<a class="navbar-item" href="https://spring.io/projects/spring-cloud">Spring Cloud</a>
<a class="navbar-item" href="https://spring.io/projects/spring-cloud-dataflow">Spring Cloud Data Flow</a>
<a class="navbar-item" href="https://spring.io/projects/spring-data">Spring Data</a>
<a class="navbar-item" href="https://spring.io/projects/spring-integration">Spring Integration</a>
<a class="navbar-item" href="https://spring.io/projects/spring-batch">Spring Batch</a>
<a class="navbar-item" href="https://spring.io/projects/spring-security">Spring Security</a>
<a class="navbar-item navbar-item-special" href="https://spring.io/projects">View all projects</a>
<a class="navbar-item" href="https://spring.io/tools">Spring Tools 4</a>
<a class="navbar-item navbar-item-special-2" href="https://start.spring.io">Spring Initializr <svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polyline points="15 10.94 15 15 1 15 1 1 5.06 1" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></polyline><polyline points="8.93 1 15 1 15 7.07" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></polyline><line x1="15" y1="1" x2="8" y2="8" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="2"></line></svg></a>
</div>
</div>
<a class="navbar-item" href="https://spring.io/training">Training</a>
<a class="navbar-item" href="https://spring.io/support">Support</a>
<div class="navbar-item has-dropdown is-hoverable is-community">
<a class="navbar-link" href="opaque-token.html#">Community</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="https://spring.io/community">Overview</a>
<a class="navbar-item" href="https://spring.io/events">Events</a>
<a class="navbar-item" href="https://spring.io/team">Team</a>
</div>
</div>
</div>
</div>
<div id="switch-theme">
<input type="checkbox" id="switch-theme-checkbox" />
<label for="switch-theme-checkbox">Dark Theme</label>
</div>
</nav>
</header>
<div class="body">
<div class="nav-container" data-component="ROOT" data-version="5.6.0">
<aside class="nav">
<div class="panels">
<div class="nav-panel-menu is-active" data-panel="menu">
<nav class="nav-menu">
<h3 class="title"><a href="../../../index.html">Spring Security</a></h3>
<ul class="nav-list">
<li class="nav-item" data-depth="0">
<ul class="nav-list">
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../index.html">Overview</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../prerequisites.html">Prerequisites</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../community.html">Community</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../whats-new.html">What&#8217;s New</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../getting-spring-security.html">Getting Spring Security</a>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../features/index.html">Features</a>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../features/authentication/index.html">Authentication</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/authentication/password-storage.html">Password Storage</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../features/exploits/index.html">Protection Against Exploits</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/exploits/csrf.html">CSRF</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/exploits/headers.html">HTTP Headers</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/exploits/http.html">HTTP Requests</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../features/integrations/index.html">Integrations</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/integrations/cryptography.html">Cryptography</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/integrations/data.html">Spring Data</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/integrations/concurrency.html">Java&#8217;s Concurrency APIs</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/integrations/jackson.html">Jackson</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../features/integrations/localization.html">Localization</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../modules.html">Project Modules</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../../../samples.html">Samples</a>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../index.html">Servlet Applications</a>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../../getting-started.html">Getting Started</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../../architecture.html">Architecture</a>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../authentication/index.html">Authentication</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/architecture.html">Authentication Architecture</a>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../authentication/passwords/index.html">Username/Password</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../authentication/passwords/input.html">Reading Username/Password</a>
<ul class="nav-list">
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/form.html">Form</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/basic.html">Basic</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/digest.html">Digest</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="4">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../authentication/passwords/storage.html">Password Storage</a>
<ul class="nav-list">
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/in-memory.html">In Memory</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/jdbc.html">JDBC</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/user-details.html">UserDetails</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/user-details-service.html">UserDetailsService</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/password-encoder.html">PasswordEncoder</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/dao-authentication-provider.html">DaoAuthenticationProvider</a>
</li>
<li class="nav-item" data-depth="5">
<a class="nav-link" href="../../authentication/passwords/ldap.html">LDAP</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/session-management.html">Session Management</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/rememberme.html">Remember Me</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/openid.html">OpenID</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/anonymous.html">Anonymous</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/preauth.html">Pre-Authentication</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/jaas.html">JAAS</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/cas.html">CAS</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/x509.html">X509</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/runas.html">Run-As</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/logout.html">Logout</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authentication/events.html">Authentication Events</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../authorization/index.html">Authorization</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authorization/architecture.html">Authorization Architecture</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authorization/authorize-requests.html">Authorize HTTP Requests</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authorization/expression-based.html">Expression-Based Access Control</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authorization/secure-objects.html">Secure Object Implementations</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authorization/method-security.html">Method Security</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../authorization/acls.html">Domain Object Security ACLs</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../index.html">OAuth2</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../login/index.html">OAuth2 Log In</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../login/core.html">Core Configuration</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../login/advanced.html">Advanced Configuration</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../client/index.html">OAuth2 Client</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../client/core.html">Core Interfaces and Classes</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../client/authorization-grants.html">OAuth2 Authorization Grants</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../client/client-authentication.html">OAuth2 Client Authentication</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../client/authorized-clients.html">OAuth2 Authorized Clients</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="index.html">OAuth2 Resource Server</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="jwt.html">JWT</a>
</li>
<li class="nav-item is-current-page" data-depth="4">
<a class="nav-link" href="opaque-token.html">Opaque Token</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="multitenancy.html">Multitenancy</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="bearer-tokens.html">Bearer Tokens</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../saml2/index.html">SAML2</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../saml2/login/index.html">SAML2 Log In</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../saml2/login/overview.html">SAML2 Log In Overview</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../saml2/login/authentication-requests.html">SAML2 Authentication Requests</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../saml2/login/authentication.html">SAML2 Authentication Responses</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../saml2/logout.html">SAML2 Logout</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../saml2/metadata.html">SAML2 Metadata</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../exploits/index.html">Protection Against Exploits</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../exploits/csrf.html">Cross Site Request Forgery (CSRF) for Servlet Environments</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../exploits/headers.html">Security HTTP Response Headers</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../exploits/http.html">HTTP</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../exploits/firewall.html">HttpFirewall</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../integrations/index.html">Integrations</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/concurrency.html">Concurrency</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/jackson.html">Jackson</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/localization.html">Localization</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/servlet-api.html">Servlet APIs</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/data.html">Spring Data</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/mvc.html">Spring MVC</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/websocket.html">WebSocket</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/cors.html">Spring&#8217;s CORS Support</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../integrations/jsp-taglibs.html">JSP Taglib</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<span class="nav-text">Configuration</span>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../configuration/java.html">Java Configuration</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../configuration/kotlin.html">Kotlin Configuration</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../configuration/xml-namespace.html">Namespace Configuration</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../test/index.html">Testing</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../test/method.html">Method Security</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../test/mockmvc/index.html">MockMvc Support</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../test/mockmvc/setup.html">MockMvc Setup</a>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../test/mockmvc/request-post-processors.html">Security RequestPostProcessors</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../test/mockmvc/authentication.html">Mocking Users</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../test/mockmvc/csrf.html">Mocking CSRF</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../test/mockmvc/form-login.html">Mocking Form Login</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../test/mockmvc/http-basic.html">Mocking HTTP Basic</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../test/mockmvc/oauth2.html">Mocking OAuth2</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../test/mockmvc/logout.html">Mocking Logout</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../test/mockmvc/request-builders.html">Security RequestBuilders</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../test/mockmvc/result-matchers.html">Security ResultMatchers</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../test/mockmvc/result-handlers.html">Security ResultHandlers</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../appendix/index.html">Appendix</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../appendix/database-schema.html">Database Schemas</a>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../appendix/namespace/index.html">XML Namespace</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../appendix/namespace/authentication-manager.html">Authentication Services</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../appendix/namespace/http.html">Web Security</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../appendix/namespace/method-security.html">Method Security</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../appendix/namespace/ldap.html">LDAP Security</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../appendix/namespace/websocket.html">WebSocket Security</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../appendix/faq.html">FAQ</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/index.html">Reactive Applications</a>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../../../reactive/getting-started.html">Getting Started</a>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<span class="nav-text">Authentication</span>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/authentication/x509.html">X.509 Authentication</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/authentication/logout.html">Logout</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<span class="nav-text">Authorization</span>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/authorization/method.html">EnableReactiveMethodSecurity</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/oauth2/index.html">OAuth2</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/oauth2/login/index.html">OAuth2 Log In</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/login/core.html">Core Configuration</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/login/advanced.html">Advanced Configuration</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/oauth2/client/index.html">OAuth2 Client</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/client/core.html">Core Interfaces and Classes</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/client/authorization-grants.html">OAuth2 Authorization Grants</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/client/client-authentication.html">OAuth2 Client Authentication</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/client/authorized-clients.html">OAuth2 Authorized Clients</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/oauth2/resource-server/index.html">OAuth2 Resource Server</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/resource-server/jwt.html">JWT</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/resource-server/opaque-token.html">Opaque Token</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/resource-server/multitenancy.html">Multitenancy</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/oauth2/resource-server/bearer-tokens.html">Bearer Tokens</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/exploits/index.html">Protection Against Exploits</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/exploits/csrf.html">CSRF</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/exploits/headers.html">Headers</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/exploits/http.html">HTTP Requests</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<span class="nav-text">Integrations</span>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/integrations/cors.html">CORS</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/integrations/rsocket.html">RSocket</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/test/index.html">Testing</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="../../../reactive/test/method.html">Testing Method Security</a>
</li>
<li class="nav-item" data-depth="3">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../../../reactive/test/web/index.html">Testing Web Security</a>
<ul class="nav-list">
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/test/web/setup.html">WebTestClient Setup</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/test/web/authentication.html">Testing Authentication</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/test/web/csrf.html">Testing CSRF</a>
</li>
<li class="nav-item" data-depth="4">
<a class="nav-link" href="../../../reactive/test/web/oauth2.html">Testing OAuth 2.0</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../../../reactive/configuration/webflux.html">WebFlux Security</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<div class="nav-panel-explore" data-panel="explore">
<div class="context">
<span class="title">Spring Security</span>
<span class="version">5.6.0</span>
</div>
<ul class="components">
<li class="component is-current">
<a class="title" href="../../../../index.html">Spring Security</a>
<ul class="versions">
<li class="version">
<a href="../../../../6.0/index.html">6.0.0-SNAPSHOT</a>
</li>
<li class="version">
<a href="../../../../6.0.0-M3/index.html">6.0.0-M3</a>
</li>
<li class="version">
<a href="../../../../6.0.0-M2/index.html">6.0.0-M2</a>
</li>
<li class="version">
<a href="../../../../6.0.0-M1/index.html">6.0.0-M1</a>
</li>
<li class="version">
<a href="../../../../5.7/index.html">5.7.0-SNAPSHOT</a>
</li>
<li class="version">
<a href="../../../../5.7.0-RC1/index.html">5.7.0-RC1</a>
</li>
<li class="version">
<a href="../../../../5.7.0-M3/index.html">5.7.0-M3</a>
</li>
<li class="version">
<a href="../../../../5.7.0-M2/index.html">5.7.0-M2</a>
</li>
<li class="version">
<a href="../../../../5.7.0-M1/index.html">5.7.0-M1</a>
</li>
<li class="version">
<a href="../../../../5.6.4/index.html">5.6.4-SNAPSHOT</a>
</li>
<li class="version is-latest">
<a href="../../../../index.html">5.6.3</a>
</li>
<li class="version">
<a href="../../../../5.6.2/index.html">5.6.2</a>
</li>
<li class="version">
<a href="../../../../5.6.1/index.html">5.6.1</a>
</li>
<li class="version is-current">
<a href="../../../index.html">5.6.0</a>
</li>
<li class="version">
<a href="../../../../5.6.0-RC1/index.html">5.6.0-RC1</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</aside>
</div>
<main class="article">
<div class="toolbar" role="navigation">
<button class="nav-toggle"></button>
<nav class="breadcrumbs" aria-label="breadcrumbs">
<ul>
<li><a href="../../../index.html">Spring Security</a></li>
<li><a href="../../index.html">Servlet Applications</a></li>
<li><a href="../index.html">OAuth2</a></li>
<li><a href="index.html">OAuth2 Resource Server</a></li>
<li><a href="opaque-token.html">Opaque Token</a></li>
</ul>
</nav>
<div class="search">
<input id="search-input" type="text" placeholder="Search docs">
</div>
<div class="page-versions">
<button class="version-menu-toggle" title="Show other versions of page">5.6.0</button>
<div class="version-menu">
<a class="version" href="../../../../6.0/servlet/oauth2/resource-server/opaque-token.html">6.0.0-SNAPSHOT</a>
<a class="version" href="../../../../6.0.0-M3/servlet/oauth2/resource-server/opaque-token.html">6.0.0-M3</a>
<a class="version" href="../../../../6.0.0-M2/servlet/oauth2/resource-server/opaque-token.html">6.0.0-M2</a>
<a class="version" href="../../../../6.0.0-M1/servlet/oauth2/resource-server/opaque-token.html">6.0.0-M1</a>
<a class="version" href="../../../../5.7/servlet/oauth2/resource-server/opaque-token.html">5.7.0-SNAPSHOT</a>
<a class="version" href="../../../../5.7.0-RC1/servlet/oauth2/resource-server/opaque-token.html">5.7.0-RC1</a>
<a class="version" href="../../../../5.7.0-M3/servlet/oauth2/resource-server/opaque-token.html">5.7.0-M3</a>
<a class="version" href="../../../../5.7.0-M2/servlet/oauth2/resource-server/opaque-token.html">5.7.0-M2</a>
<a class="version" href="../../../../5.7.0-M1/servlet/oauth2/resource-server/opaque-token.html">5.7.0-M1</a>
<a class="version" href="../../../../5.6.4/servlet/oauth2/resource-server/opaque-token.html">5.6.4-SNAPSHOT</a>
<a class="version" href="../../../../servlet/oauth2/resource-server/opaque-token.html">5.6.3</a>
<a class="version" href="../../../../5.6.2/servlet/oauth2/resource-server/opaque-token.html">5.6.2</a>
<a class="version" href="../../../../5.6.1/servlet/oauth2/resource-server/opaque-token.html">5.6.1</a>
<a class="version is-current" href="opaque-token.html">5.6.0</a>
<a class="version is-missing" href="../../../../5.6.0-RC1/index.html">5.6.0-RC1</a>
</div>
</div>
<div class="edit-this-page"><a href="https://github.com/spring-projects/spring-security/blob/5.6.0/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc">Edit this Page</a></div>
</div>
<div class="content">
<aside class="toc sidebar" data-title="Contents" data-levels="2">
<div class="toc-menu"></div>
</aside>
<article class="doc">
<div class="admonitionblock important">
<table>
<tbody><tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p> For the latest stable version, please use <a href="../../../../servlet/oauth2/resource-server/opaque-token.html">Spring Security 5.6.3</a>!</p>
</div>
</td>
</tr></tbody>
</table>
</div>
<h1 id="page-title" class="page">OAuth 2.0 Resource Server Opaque Token</h1>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-minimaldependencies"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-minimaldependencies"></a>Minimal Dependencies for Introspection</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As described in <a href="jwt.html#oauth2resourceserver-jwt-minimaldependencies" class="xref page">Minimal Dependencies for JWT</a> most of Resource Server support is collected in <code>spring-security-oauth2-resource-server</code>.
However unless a custom <a href="opaque-token.html#oauth2resourceserver-opaque-introspector"><code>OpaqueTokenIntrospector</code></a> is provided, the Resource Server will fallback to NimbusOpaqueTokenIntrospector.
Meaning that both <code>spring-security-oauth2-resource-server</code> and <code>oauth2-oidc-sdk</code> are necessary in order to have a working minimal Resource Server that supports opaque Bearer Tokens.
Please refer to <code>spring-security-oauth2-resource-server</code> in order to determin the correct version for <code>oauth2-oidc-sdk</code>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-minimalconfiguration"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-minimalconfiguration"></a>Minimal Configuration for Introspection</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Typically, an opaque token can be verified via an <a href="https://tools.ietf.org/html/rfc7662">OAuth 2.0 Introspection Endpoint</a>, hosted by the authorization server.
This can be handy when revocation is a requirement.</p>
</div>
<div class="paragraph">
<p>When using <a href="https://spring.io/projects/spring-boot">Spring Boot</a>, configuring an application as a resource server that uses introspection consists of two basic steps.
First, include the needed dependencies and second, indicate the introspection endpoint details.</p>
</div>
<div class="sect2">
<h3 id="oauth2resourceserver-opaque-introspectionuri"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-introspectionuri"></a>Specifying the Authorization Server</h3>
<div class="paragraph">
<p>To specify where the introspection endpoint is, simply do:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">security:
  oauth2:
    resourceserver:
      opaque-token:
        introspection-uri: https://idp.example.com/introspect
        client-id: client
        client-secret: secret</code></pre>
</div>
</div>
<div class="paragraph">
<p>Where <code><a href="https://idp.example.com/introspect" class="bare">https://idp.example.com/introspect</a></code> is the introspection endpoint hosted by your authorization server and <code>client-id</code> and <code>client-secret</code> are the credentials needed to hit that endpoint.</p>
</div>
<div class="paragraph">
<p>Resource Server will use these properties to further self-configure and subsequently validate incoming JWTs.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
When using introspection, the authorization server&#8217;s word is the law.
If the authorization server responses that the token is valid, then it is.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And that&#8217;s it!</p>
</div>
</div>
<div class="sect2">
<h3 id="_startup_expectations"><a class="anchor" href="opaque-token.html#_startup_expectations"></a>Startup Expectations</h3>
<div class="paragraph">
<p>When this property and these dependencies are used, Resource Server will automatically configure itself to validate Opaque Bearer Tokens.</p>
</div>
<div class="paragraph">
<p>This startup process is quite a bit simpler than for JWTs since no endpoints need to be discovered and no additional validation rules get added.</p>
</div>
</div>
<div class="sect2">
<h3 id="_runtime_expectations"><a class="anchor" href="opaque-token.html#_runtime_expectations"></a>Runtime Expectations</h3>
<div class="paragraph">
<p>Once the application is started up, Resource Server will attempt to process any request containing an <code>Authorization: Bearer</code> header:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-http hljs" data-lang="http">GET / HTTP/1.1
Authorization: Bearer some-token-value # Resource Server will process this</code></pre>
</div>
</div>
<div class="paragraph">
<p>So long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification.</p>
</div>
<div class="paragraph">
<p>Given an Opaque Token, Resource Server will</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Query the provided introspection endpoint using the provided credentials and the token</p>
</li>
<li>
<p>Inspect the response for an <code>{ 'active' : true }</code> attribute</p>
</li>
<li>
<p>Map each scope to an authority with the prefix <code>SCOPE_</code></p>
</li>
</ol>
</div>
<div class="paragraph">
<p>The resulting <code>Authentication#getPrincipal</code>, by default, is a Spring Security <code><a href="https://docs.spring.io/spring-security/site/docs/5.6.0/api/org/springframework/security/oauth2/core/OAuth2AuthenticatedPrincipal.html">OAuth2AuthenticatedPrincipal</a></code> object, and <code>Authentication#getName</code> maps to the token&#8217;s <code>sub</code> property, if one is present.</p>
</div>
<div class="paragraph">
<p>From here, you may want to jump to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="opaque-token.html#oauth2resourceserver-opaque-architecture">How Opaque Token Authentication Works</a></p>
</li>
<li>
<p><a href="opaque-token.html#oauth2resourceserver-opaque-attributes">Looking Up Attributes Post-Authentication</a></p>
</li>
<li>
<p><a href="opaque-token.html#oauth2resourceserver-opaque-authorization-extraction">Extracting Authorities Manually</a></p>
</li>
<li>
<p><a href="opaque-token.html#oauth2resourceserver-opaque-jwt-introspector">Using Introspection with JWTs</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-architecture"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-architecture"></a>How Opaque Token Authentication Works</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Next, let&#8217;s see the architectural components that Spring Security uses to support <a href="https://tools.ietf.org/html/rfc7662">opaque token</a> Authentication in servlet-based applications, like the one we just saw.</p>
</div>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring-security/site/docs/5.6.0/api/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html"><code>OpaqueTokenAuthenticationProvider</code></a> is an <a href="../../authentication/architecture.html#servlet-authentication-authenticationprovider" class="xref page"><code>AuthenticationProvider</code></a> implementation that leverages a <a href="opaque-token.html#oauth2resourceserver-opaque-introspector"><code>OpaqueTokenIntrospector</code></a> to authenticate an opaque token.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s take a look at how <code>OpaqueTokenAuthenticationProvider</code> works within Spring Security.
The figure explains details of how the <a href="../../authentication/architecture.html#servlet-authentication-authenticationmanager" class="xref page"><code>AuthenticationManager</code></a> in figures from <a href="opaque-token.html#oauth2resourceserver-authentication-bearertokenauthenticationfilter">Reading the Bearer Token</a> works.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../../../_images/servlet/oauth2/opaquetokenauthenticationprovider.png" alt="opaquetokenauthenticationprovider">
</div>
<div class="title">Figure 1. <code>OpaqueTokenAuthenticationProvider</code> Usage</div>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../../_images/icons/number_1.png" alt="number 1"></span> The authentication <code>Filter</code> from <a href="opaque-token.html#oauth2resourceserver-authentication-bearertokenauthenticationfilter">Reading the Bearer Token</a> passes a <code>BearerTokenAuthenticationToken</code> to the <code>AuthenticationManager</code> which is implemented by <a href="../../authentication/architecture.html#servlet-authentication-providermanager" class="xref page"><code>ProviderManager</code></a>.</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../../_images/icons/number_2.png" alt="number 2"></span> The <code>ProviderManager</code> is configured to use an <a href="../../authentication/architecture.html#servlet-authentication-authenticationprovider" class="xref page">AuthenticationProvider</a> of type <code>OpaqueTokenAuthenticationProvider</code>.</p>
</div>
<div id="oauth2resourceserver-opaque-architecture-introspector" class="paragraph">
<p><span class="image"><img src="../../../_images/icons/number_3.png" alt="number 3"></span> <code>OpaqueTokenAuthenticationProvider</code> introspects the opaque token and adds granted authorities using an <a href="opaque-token.html#oauth2resourceserver-opaque-introspector"><code>OpaqueTokenIntrospector</code></a>.
When authentication is successful, the <a href="../../authentication/architecture.html#servlet-authentication-authentication" class="xref page"><code>Authentication</code></a> that is returned is of type <code>BearerTokenAuthentication</code> and has a principal that is the <code>OAuth2AuthenticatedPrincipal</code> returned by the configured <a href="opaque-token.html#oauth2resourceserver-opaque-introspector"><code>OpaqueTokenIntrospector</code></a>.
Ultimately, the returned <code>BearerTokenAuthentication</code> will be set on the <a href="../../authentication/architecture.html#servlet-authentication-securitycontextholder" class="xref page"><code>SecurityContextHolder</code></a> by the authentication <code>Filter</code>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-attributes"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-attributes"></a>Looking Up Attributes Post-Authentication</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Once a token is authenticated, an instance of <code>BearerTokenAuthentication</code> is set in the <code>SecurityContext</code>.</p>
</div>
<div class="paragraph">
<p>This means that it&#8217;s available in <code>@Controller</code> methods when using <code>@EnableWebMvc</code> in your configuration:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@GetMapping("/foo")
public String foo(BearerTokenAuthentication authentication) {
    return authentication.getTokenAttributes().get("sub") + " is the subject";
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@GetMapping("/foo")
fun foo(authentication: BearerTokenAuthentication): String {
    return authentication.tokenAttributes["sub"].toString() + " is the subject"
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Since <code>BearerTokenAuthentication</code> holds an <code>OAuth2AuthenticatedPrincipal</code>, that also means that it&#8217;s available to controller methods, too:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@GetMapping("/foo")
public String foo(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {
    return principal.getAttribute("sub") + " is the subject";
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@GetMapping("/foo")
fun foo(@AuthenticationPrincipal principal: OAuth2AuthenticatedPrincipal): String {
    return principal.getAttribute&lt;Any&gt;("sub").toString() + " is the subject"
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_looking_up_attributes_via_spel"><a class="anchor" href="opaque-token.html#_looking_up_attributes_via_spel"></a>Looking Up Attributes Via SpEL</h3>
<div class="paragraph">
<p>Of course, this also means that attributes can be accessed via SpEL.</p>
</div>
<div class="paragraph">
<p>For example, if using <code>@EnableGlobalMethodSecurity</code> so that you can use <code>@PreAuthorize</code> annotations, you can do:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@PreAuthorize("principal?.attributes['sub'] == 'foo'")
public String forFoosEyesOnly() {
    return "foo";
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@PreAuthorize("principal?.attributes['sub'] == 'foo'")
fun forFoosEyesOnly(): String {
    return "foo"
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-sansboot"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-sansboot"></a>Overriding or Replacing Boot Auto Configuration</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There are two <code>@Bean</code>s that Spring Boot generates on Resource Server&#8217;s behalf.</p>
</div>
<div class="paragraph">
<p>The first is a <code>WebSecurityConfigurerAdapter</code> that configures the app as a resource server.
When use Opaque Token, this <code>WebSecurityConfigurerAdapter</code> looks like:</p>
</div>
<div class="exampleblock">
<div class="title">Example 1. Default Opaque Token Configuration</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">protected void configure(HttpSecurity http) {
    http
        .authorizeHttpRequests(authorize -&gt; authorize
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">override fun configure(http: HttpSecurity) {
    http {
        authorizeRequests {
            authorize(anyRequest, authenticated)
        }
        oauth2ResourceServer {
            opaqueToken { }
        }
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>If the application doesn&#8217;t expose a <code>WebSecurityConfigurerAdapter</code> bean, then Spring Boot will expose the above default one.</p>
</div>
<div class="paragraph">
<p>Replacing this is as simple as exposing the bean within the application:</p>
</div>
<div class="exampleblock">
<div class="title">Example 2. Custom Opaque Token Configuration</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@EnableWebSecurity
public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeHttpRequests(authorize -&gt; authorize
                .mvcMatchers("/messages/**").hasAuthority("SCOPE_message:read")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -&gt; oauth2
                .opaqueToken(opaqueToken -&gt; opaqueToken
                    .introspector(myIntrospector())
                )
            );
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@EnableWebSecurity
class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http {
            authorizeRequests {
                authorize("/messages/**", hasAuthority("SCOPE_message:read"))
                authorize(anyRequest, authenticated)
            }
            oauth2ResourceServer {
                opaqueToken {
                    introspector = myIntrospector()
                }
            }
        }
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>The above requires the scope of <code>message:read</code> for any URL that starts with <code>/messages/</code>.</p>
</div>
<div class="paragraph">
<p>Methods on the <code>oauth2ResourceServer</code> DSL will also override or replace auto configuration.</p>
</div>
<div id="oauth2resourceserver-opaque-introspector" class="paragraph">
<p>For example, the second <code>@Bean</code> Spring Boot creates is an <code>OpaqueTokenIntrospector</code>, <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector">which decodes <code>String</code> tokens into validated instances of <code>OAuth2AuthenticatedPrincipal</code></a>:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
public OpaqueTokenIntrospector introspector() {
    return new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@Bean
fun introspector(): OpaqueTokenIntrospector {
    return NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret)
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>If the application doesn&#8217;t expose a <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a> bean, then Spring Boot will expose the above default one.</p>
</div>
<div class="paragraph">
<p>And its configuration can be overridden using <code>introspectionUri()</code> and <code>introspectionClientCredentials()</code> or replaced using <code>introspector()</code>.</p>
</div>
<div class="paragraph">
<p>Or, if you&#8217;re not using Spring Boot at all, then both of these components - the filter chain and a <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a> can be specified in XML.</p>
</div>
<div class="paragraph">
<p>The filter chain is specified like so:</p>
</div>
<div class="exampleblock">
<div class="title">Example 3. Default Opaque Token Configuration</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Xml</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;http&gt;
    &lt;intercept-uri pattern="/**" access="authenticated"/&gt;
    &lt;oauth2-resource-server&gt;
        &lt;opaque-token introspector-ref="opaqueTokenIntrospector"/&gt;
    &lt;/oauth2-resource-server&gt;
&lt;/http&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>And the <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a> like so:</p>
</div>
<div class="exampleblock">
<div class="title">Example 4. Opaque Token Introspector</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Xml</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;bean id="opaqueTokenIntrospector"
        class="org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector"&gt;
    &lt;constructor-arg value="${spring.security.oauth2.resourceserver.opaquetoken.introspection_uri}"/&gt;
    &lt;constructor-arg value="${spring.security.oauth2.resourceserver.opaquetoken.client_id}"/&gt;
    &lt;constructor-arg value="${spring.security.oauth2.resourceserver.opaquetoken.client_secret}"/&gt;
&lt;/bean&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="oauth2resourceserver-opaque-introspectionuri-dsl"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-introspectionuri-dsl"></a>Using <code>introspectionUri()</code></h3>
<div class="paragraph">
<p>An authorization server&#8217;s Introspection Uri can be configured <a href="opaque-token.html#oauth2resourceserver-opaque-introspectionuri">as a configuration property</a> or it can be supplied in the DSL:</p>
</div>
<div class="exampleblock">
<div class="title">Example 5. Introspection URI Configuration</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@EnableWebSecurity
public class DirectlyConfiguredIntrospectionUri extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeHttpRequests(authorize -&gt; authorize
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -&gt; oauth2
                .opaqueToken(opaqueToken -&gt; opaqueToken
                    .introspectionUri("https://idp.example.com/introspect")
                    .introspectionClientCredentials("client", "secret")
                )
            );
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@EnableWebSecurity
class DirectlyConfiguredIntrospectionUri : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2ResourceServer {
                opaqueToken {
                    introspectionUri = "https://idp.example.com/introspect"
                    introspectionClientCredentials("client", "secret")
                }
            }
        }
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Xml</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;bean id="opaqueTokenIntrospector"
        class="org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector"&gt;
    &lt;constructor-arg value="https://idp.example.com/introspect"/&gt;
    &lt;constructor-arg value="client"/&gt;
    &lt;constructor-arg value="secret"/&gt;
&lt;/bean&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Using <code>introspectionUri()</code> takes precedence over any configuration property.</p>
</div>
</div>
<div class="sect2">
<h3 id="oauth2resourceserver-opaque-introspector-dsl"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-introspector-dsl"></a>Using <code>introspector()</code></h3>
<div class="paragraph">
<p>More powerful than <code>introspectionUri()</code> is <code>introspector()</code>, which will completely replace any Boot auto configuration of <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a>:</p>
</div>
<div class="exampleblock">
<div class="title">Example 6. Introspector Configuration</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@EnableWebSecurity
public class DirectlyConfiguredIntrospector extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeHttpRequests(authorize -&gt; authorize
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -&gt; oauth2
                .opaqueToken(opaqueToken -&gt; opaqueToken
                    .introspector(myCustomIntrospector())
                )
            );
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@EnableWebSecurity
class DirectlyConfiguredIntrospector : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2ResourceServer {
                opaqueToken {
                    introspector = myCustomIntrospector()
                }
            }
        }
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Xml</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;http&gt;
    &lt;intercept-uri pattern="/**" access="authenticated"/&gt;
    &lt;oauth2-resource-server&gt;
        &lt;opaque-token introspector-ref="myCustomIntrospector"/&gt;
    &lt;/oauth2-resource-server&gt;
&lt;/http&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>This is handy when deeper configuration, like <a href="opaque-token.html#oauth2resourceserver-opaque-authorization-extraction">authority mapping</a>, <a href="opaque-token.html#oauth2resourceserver-opaque-jwt-introspector">JWT revocation</a>, or <a href="opaque-token.html#oauth2resourceserver-opaque-timeouts">request timeouts</a>, is necessary.</p>
</div>
</div>
<div class="sect2">
<h3 id="oauth2resourceserver-opaque-introspector-bean"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-introspector-bean"></a>Exposing a <code>OpaqueTokenIntrospector</code> <code>@Bean</code></h3>
<div class="paragraph">
<p>Or, exposing a <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a> <code>@Bean</code> has the same effect as <code>introspector()</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
public OpaqueTokenIntrospector introspector() {
    return new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-authorization"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-authorization"></a>Configuring Authorization</h2>
<div class="sectionbody">
<div class="paragraph">
<p>An OAuth 2.0 Introspection endpoint will typically return a <code>scope</code> attribute, indicating the scopes (or authorities) it&#8217;s been granted, for example:</p>
</div>
<div class="paragraph">
<p><code>{ &#8230;&#8203;, "scope" : "messages contacts"}</code></p>
</div>
<div class="paragraph">
<p>When this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string "SCOPE_".</p>
</div>
<div class="paragraph">
<p>This means that to protect an endpoint or method with a scope derived from an Opaque Token, the corresponding expressions should include this prefix:</p>
</div>
<div class="exampleblock">
<div class="title">Example 7. Authorization Opaque Token Configuration</div>
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@EnableWebSecurity
public class MappedAuthorities extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeHttpRequests(authorizeRequests -&gt; authorizeRequests
                .mvcMatchers("/contacts/**").hasAuthority("SCOPE_contacts")
                .mvcMatchers("/messages/**").hasAuthority("SCOPE_messages")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@EnableWebSecurity
class MappedAuthorities : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
       http {
            authorizeRequests {
                authorize("/contacts/**", hasAuthority("SCOPE_contacts"))
                authorize("/messages/**", hasAuthority("SCOPE_messages"))
                authorize(anyRequest, authenticated)
            }
           oauth2ResourceServer {
               opaqueToken { }
           }
        }
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Xml</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;http&gt;
    &lt;intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/&gt;
    &lt;intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/&gt;
    &lt;oauth2-resource-server&gt;
        &lt;opaque-token introspector-ref="opaqueTokenIntrospector"/&gt;
    &lt;/oauth2-resource-server&gt;
&lt;/http&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Or similarly with method security:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@PreAuthorize("hasAuthority('SCOPE_messages')")
public List&lt;Message&gt; getMessages(...) {}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@PreAuthorize("hasAuthority('SCOPE_messages')")
fun getMessages(): List&lt;Message?&gt; {}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="oauth2resourceserver-opaque-authorization-extraction"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-authorization-extraction"></a>Extracting Authorities Manually</h3>
<div class="paragraph">
<p>By default, Opaque Token support will extract the scope claim from an introspection response and parse it into individual <code>GrantedAuthority</code> instances.</p>
</div>
<div class="paragraph">
<p>For example, if the introspection response were:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
    "active" : true,
    "scope" : "message:read message:write"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then Resource Server would generate an <code>Authentication</code> with two authorities, one for <code>message:read</code> and the other for <code>message:write</code>.</p>
</div>
<div class="paragraph">
<p>This can, of course, be customized using a custom <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a> that takes a look at the attribute set and converts in its own way:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
    private OpaqueTokenIntrospector delegate =
            new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");

    public OAuth2AuthenticatedPrincipal introspect(String token) {
        OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);
        return new DefaultOAuth2AuthenticatedPrincipal(
                principal.getName(), principal.getAttributes(), extractAuthorities(principal));
    }

    private Collection&lt;GrantedAuthority&gt; extractAuthorities(OAuth2AuthenticatedPrincipal principal) {
        List&lt;String&gt; scopes = principal.getAttribute(OAuth2IntrospectionClaimNames.SCOPE);
        return scopes.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">class CustomAuthoritiesOpaqueTokenIntrospector : OpaqueTokenIntrospector {
    private val delegate: OpaqueTokenIntrospector = NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret")
    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {
        val principal: OAuth2AuthenticatedPrincipal = delegate.introspect(token)
        return DefaultOAuth2AuthenticatedPrincipal(
                principal.name, principal.attributes, extractAuthorities(principal))
    }

    private fun extractAuthorities(principal: OAuth2AuthenticatedPrincipal): Collection&lt;GrantedAuthority&gt; {
        val scopes: List&lt;String&gt; = principal.getAttribute(OAuth2IntrospectionClaimNames.SCOPE)
        return scopes
                .map { SimpleGrantedAuthority(it) }
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Thereafter, this custom introspector can be configured simply by exposing it as a <code>@Bean</code>:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
public OpaqueTokenIntrospector introspector() {
    return new CustomAuthoritiesOpaqueTokenIntrospector();
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@Bean
fun introspector(): OpaqueTokenIntrospector {
    return CustomAuthoritiesOpaqueTokenIntrospector()
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-timeouts"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-timeouts"></a>Configuring Timeouts</h2>
<div class="sectionbody">
<div class="paragraph">
<p>By default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server.</p>
</div>
<div class="paragraph">
<p>This may be too short in some scenarios.
Further, it doesn&#8217;t take into account more sophisticated patterns like back-off and discovery.</p>
</div>
<div class="paragraph">
<p>To adjust the way in which Resource Server connects to the authorization server, <code>NimbusOpaqueTokenIntrospector</code> accepts an instance of <code>RestOperations</code>:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
public OpaqueTokenIntrospector introspector(RestTemplateBuilder builder, OAuth2ResourceServerProperties properties) {
    RestOperations rest = builder
            .basicAuthentication(properties.getOpaquetoken().getClientId(), properties.getOpaquetoken().getClientSecret())
            .setConnectTimeout(Duration.ofSeconds(60))
            .setReadTimeout(Duration.ofSeconds(60))
            .build();

    return new NimbusOpaqueTokenIntrospector(introspectionUri, rest);
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@Bean
fun introspector(builder: RestTemplateBuilder, properties: OAuth2ResourceServerProperties): OpaqueTokenIntrospector? {
    val rest: RestOperations = builder
            .basicAuthentication(properties.opaquetoken.clientId, properties.opaquetoken.clientSecret)
            .setConnectTimeout(Duration.ofSeconds(60))
            .setReadTimeout(Duration.ofSeconds(60))
            .build()
    return NimbusOpaqueTokenIntrospector(introspectionUri, rest)
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-jwt-introspector"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-jwt-introspector"></a>Using Introspection with JWTs</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A common question is whether or not introspection is compatible with JWTs.
Spring Security&#8217;s Opaque Token support has been designed to not care about the format of the token&#8201;&#8212;&#8201;it will gladly pass any token to the introspection endpoint provided.</p>
</div>
<div class="paragraph">
<p>So, let&#8217;s say that you&#8217;ve got a requirement that requires you to check with the authorization server on each request, in case the JWT has been revoked.</p>
</div>
<div class="paragraph">
<p>Even though you are using the JWT format for the token, your validation method is introspection, meaning you&#8217;d want to do:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring:
  security:
    oauth2:
      resourceserver:
        opaque-token:
          introspection-uri: https://idp.example.org/introspection
          client-id: client
          client-secret: secret</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this case, the resulting <code>Authentication</code> would be <code>BearerTokenAuthentication</code>.
Any attributes in the corresponding <code>OAuth2AuthenticatedPrincipal</code> would be whatever was returned by the introspection endpoint.</p>
</div>
<div class="paragraph">
<p>But, let&#8217;s say that, oddly enough, the introspection endpoint only returns whether or not the token is active.
Now what?</p>
</div>
<div class="paragraph">
<p>In this case, you can create a custom <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a> that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
    private OpaqueTokenIntrospector delegate =
            new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
    private JwtDecoder jwtDecoder = new NimbusJwtDecoder(new ParseOnlyJWTProcessor());

    public OAuth2AuthenticatedPrincipal introspect(String token) {
        OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);
        try {
            Jwt jwt = this.jwtDecoder.decode(token);
            return new DefaultOAuth2AuthenticatedPrincipal(jwt.getClaims(), NO_AUTHORITIES);
        } catch (JwtException ex) {
            throw new OAuth2IntrospectionException(ex);
        }
    }

    private static class ParseOnlyJWTProcessor extends DefaultJWTProcessor&lt;SecurityContext&gt; {
    	JWTClaimsSet process(SignedJWT jwt, SecurityContext context)
                throws JOSEException {
            return jwt.getJWTClaimsSet();
        }
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">class JwtOpaqueTokenIntrospector : OpaqueTokenIntrospector {
    private val delegate: OpaqueTokenIntrospector = NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret")
    private val jwtDecoder: JwtDecoder = NimbusJwtDecoder(ParseOnlyJWTProcessor())
    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {
        val principal = delegate.introspect(token)
        return try {
            val jwt: Jwt = jwtDecoder.decode(token)
            DefaultOAuth2AuthenticatedPrincipal(jwt.claims, NO_AUTHORITIES)
        } catch (ex: JwtException) {
            throw OAuth2IntrospectionException(ex.message)
        }
    }

    private class ParseOnlyJWTProcessor : DefaultJWTProcessor&lt;SecurityContext&gt;() {
        override fun process(jwt: SignedJWT, context: SecurityContext): JWTClaimsSet {
            return jwt.jwtClaimsSet
        }
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Thereafter, this custom introspector can be configured simply by exposing it as a <code>@Bean</code>:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
public OpaqueTokenIntrospector introspector() {
    return new JwtOpaqueTokenIntrospector();
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@Bean
fun introspector(): OpaqueTokenIntrospector {
    return JwtOpaqueTokenIntrospector()
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="oauth2resourceserver-opaque-userinfo"><a class="anchor" href="opaque-token.html#oauth2resourceserver-opaque-userinfo"></a>Calling a <code>/userinfo</code> Endpoint</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Generally speaking, a Resource Server doesn&#8217;t care about the underlying user, but instead about the authorities that have been granted.</p>
</div>
<div class="paragraph">
<p>That said, at times it can be valuable to tie the authorization statement back to a user.</p>
</div>
<div class="paragraph">
<p>If an application is also using <code>spring-security-oauth2-client</code>, having set up the appropriate <code>ClientRegistrationRepository</code>, then this is quite simple with a custom <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a>.
This implementation below does three things:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Delegates to the introspection endpoint, to affirm the token&#8217;s validity</p>
</li>
<li>
<p>Looks up the appropriate client registration associated with the <code>/userinfo</code> endpoint</p>
</li>
<li>
<p>Invokes and returns the response from the <code>/userinfo</code> endpoint</p>
</li>
</ul>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
    private final OpaqueTokenIntrospector delegate =
            new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
    private final OAuth2UserService oauth2UserService = new DefaultOAuth2UserService();

    private final ClientRegistrationRepository repository;

    // ... constructor

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        OAuth2AuthenticatedPrincipal authorized = this.delegate.introspect(token);
        Instant issuedAt = authorized.getAttribute(ISSUED_AT);
        Instant expiresAt = authorized.getAttribute(EXPIRES_AT);
        ClientRegistration clientRegistration = this.repository.findByRegistrationId("registration-id");
        OAuth2AccessToken token = new OAuth2AccessToken(BEARER, token, issuedAt, expiresAt);
        OAuth2UserRequest oauth2UserRequest = new OAuth2UserRequest(clientRegistration, token);
        return this.oauth2UserService.loadUser(oauth2UserRequest);
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">class UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {
    private val delegate: OpaqueTokenIntrospector = NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret")
    private val oauth2UserService = DefaultOAuth2UserService()
    private val repository: ClientRegistrationRepository? = null

    // ... constructor

    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {
        val authorized = delegate.introspect(token)
        val issuedAt: Instant? = authorized.getAttribute(ISSUED_AT)
        val expiresAt: Instant? = authorized.getAttribute(EXPIRES_AT)
        val clientRegistration: ClientRegistration = repository!!.findByRegistrationId("registration-id")
        val accessToken = OAuth2AccessToken(BEARER, token, issuedAt, expiresAt)
        val oauth2UserRequest = OAuth2UserRequest(clientRegistration, accessToken)
        return oauth2UserService.loadUser(oauth2UserRequest)
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>If you aren&#8217;t using <code>spring-security-oauth2-client</code>, it&#8217;s still quite simple.
You will simply need to invoke the <code>/userinfo</code> with your own instance of <code>WebClient</code>:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
    private final OpaqueTokenIntrospector delegate =
            new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
    private final WebClient rest = WebClient.create();

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        OAuth2AuthenticatedPrincipal authorized = this.delegate.introspect(token);
        return makeUserInfoRequest(authorized);
    }
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">class UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {
    private val delegate: OpaqueTokenIntrospector = NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret")
    private val rest: WebClient = WebClient.create()

    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {
        val authorized = delegate.introspect(token)
        return makeUserInfoRequest(authorized)
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Either way, having created your <a href="opaque-token.html#oauth2resourceserver-opaque-architecture-introspector"><code>OpaqueTokenIntrospector</code></a>, you should publish it as a <code>@Bean</code> to override the defaults:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock primary">
<div class="title">Java</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
OpaqueTokenIntrospector introspector() {
    return new UserInfoOpaqueTokenIntrospector(...);
}</code></pre>
</div>
</div>
<div class="listingblock secondary">
<div class="title">Kotlin</div>
<div class="content">
<pre class="highlightjs highlight"><code class="language-kotlin hljs" data-lang="kotlin">@Bean
fun introspector(): OpaqueTokenIntrospector {
    return UserInfoOpaqueTokenIntrospector(...)
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<nav class="pagination">
<span class="prev"><a href="jwt.html">JWT</a></span>
<span class="next"><a href="multitenancy.html">Multitenancy</a></span>
</nav>
</article>
</div>
</main>
</div>
<footer class="footer flex">
<div id="spring-links flex">
<img id="springlogo" src="../../../../_/img/spring-logo.svg" alt="Spring">
<p class="smallest antialiased">© <script>var d = new Date();
        document.write(d.getFullYear());</script> <a href="https://www.vmware.com/">VMware</a>, Inc. or its affiliates. <a href="https://www.vmware.com/help/legal.html">Terms of Use</a> • <a href="https://www.vmware.com/help/privacy.html" rel="noopener noreferrer">Privacy</a> • <a href="https://spring.io/trademarks">Trademark Guidelines</a> <span id="thank-you-mobile">• <a href="https://spring.io/thank-you">Thank you</a></span> • <a href="https://www.vmware.com/help/privacy/california-privacy-rights.html">Your California Privacy Rights</a> • <a class="ot-sdk-show-settings">Cookie Settings</a> <span id="teconsent"></span></p>
<p class="smallest antialiased">Apache®, Apache Tomcat®, Apache Kafka®, Apache Cassandra&trade;, and Apache Geode&trade; are trademarks or registered trademarks of the Apache Software Foundation in the United States and/or other countries. Java&trade;, Java&trade; SE, Java&trade; EE, and OpenJDK&trade; are trademarks of Oracle and/or its affiliates. Kubernetes® is a registered trademark of the Linux Foundation in the United States and other countries. Linux® is the registered trademark of Linus Torvalds in the United States and other countries. Windows® and Microsoft® Azure are registered trademarks of Microsoft Corporation. “AWS” and “Amazon Web Services” are trademarks or registered trademarks of Amazon.com Inc. or its affiliates. All other trademarks and copyrights are property of their respective owners and are only mentioned for informative purposes. Other names may be trademarks of their respective owners.</p>
</div>
<div id="social-icons" class="flex jc-between">
<a href="https://www.youtube.com/user/SpringSourceDev" title="Youtube"><svg id="youtube-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><circle class="cls-1" cx="20" cy="20" r="20" /><path class="cls-2" d="M30.91,14.53a2.89,2.89,0,0,0-2-2C27.12,12,20,12,20,12s-7.12,0-8.9.47a2.9,2.9,0,0,0-2,2A30.56,30.56,0,0,0,8.63,20a30.44,30.44,0,0,0,.46,5.47,2.89,2.89,0,0,0,2,2C12.9,28,20,28,20,28s7.12,0,8.9-.47a2.87,2.87,0,0,0,2-2A30.56,30.56,0,0,0,31.37,20,28.88,28.88,0,0,0,30.91,14.53ZM17.73,23.41V16.59L23.65,20Z" /></svg></a>
<a href="https://github.com/spring-projects" title="Github"><svg id="github-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 75.93 75.93"><path class="cls-1" d="M38,0a38,38,0,1,0,38,38A38,38,0,0,0,38,0Z" /></g><path class="cls-2" d="M38,15.59A22.95,22.95,0,0,0,30.71,60.3c1.15.21,1.57-.5,1.57-1.11s0-2,0-3.9c-6.38,1.39-7.73-3.07-7.73-3.07A6.09,6.09,0,0,0,22,48.86c-2.09-1.42.15-1.39.15-1.39a4.81,4.81,0,0,1,3.52,2.36c2,3.5,5.37,2.49,6.67,1.91a4.87,4.87,0,0,1,1.46-3.07c-5.09-.58-10.45-2.55-10.45-11.34a8.84,8.84,0,0,1,2.36-6.15,8.29,8.29,0,0,1,.23-6.07s1.92-.62,6.3,2.35a21.82,21.82,0,0,1,11.49,0c4.38-3,6.3-2.35,6.3-2.35a8.29,8.29,0,0,1,.23,6.07,8.84,8.84,0,0,1,2.36,6.15c0,8.81-5.37,10.75-10.48,11.32a5.46,5.46,0,0,1,1.56,4.25c0,3.07,0,5.54,0,6.29s.42,1.33,1.58,1.1A22.94,22.94,0,0,0,38,15.59Z" /></svg></a>
<a href="https://twitter.com/springcentral" title="Twitter"><svg id="twitter-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 75.93 75.93"><circle class="cls-1" cx="37.97" cy="37.97" r="37.97" /><path id="Twitter-2" data-name="Twitter" class="cls-2" d="M55.2,22.73a15.43,15.43,0,0,1-4.88,1.91,7.56,7.56,0,0,0-5.61-2.49A7.78,7.78,0,0,0,37,30a7.56,7.56,0,0,0,.2,1.79,21.63,21.63,0,0,1-15.84-8.23,8,8,0,0,0,2.37,10.52,7.66,7.66,0,0,1-3.48-1v.09A7.84,7.84,0,0,0,26.45,41a7.54,7.54,0,0,1-2,.28A7.64,7.64,0,0,1,23,41.09a7.71,7.71,0,0,0,7.18,5.47,15.21,15.21,0,0,1-9.55,3.37,15.78,15.78,0,0,1-1.83-.11,21.41,21.41,0,0,0,11.78,3.54c14.13,0,21.86-12,21.86-22.42,0-.34,0-.68,0-1a15.67,15.67,0,0,0,3.83-4.08,14.9,14.9,0,0,1-4.41,1.24A7.8,7.8,0,0,0,55.2,22.73Z" /></svg></a>
</div>
</footer>
<script src="../../../../_/js/site.js"></script>
<script async src="../../../../_/js/vendor/highlight.js"></script>
<script async src="../../../../_/js/vendor/tabs.js"></script>
<script src="../../../../_/js/vendor/switchtheme.js"></script>
<script src="../../../../_/js/vendor/docsearch.min.js"></script>

<script>
var search = docsearch({
  appId: '244V8V9FGG',
  apiKey: '82c7ead946afbac3cf98c32446154691',
  indexName: 'security-docs',
  inputSelector: '#search-input',
  autocompleteOptions: { hint: false, keyboardShortcuts: ['s'] },
  algoliaOptions: { hitsPerPage: 10 }
}).autocomplete
search.on('autocomplete:closed', function () { search.autocomplete.setVal() })
</script>
<script>if (window.parent == window) {(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create', 'UA-2728886-23', 'auto', {'siteSpeedSampleRate': 100});ga('send', 'pageview');}</script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"702d447acf27642c","token":"bffcb8a918ae4755926f76178bfbd26b","version":"2021.12.0","si":100}' crossorigin="anonymous"></script>
</body>
</html>
